package com.gcxy.security.handler;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.gcxy.config.warpper.BufferedRequestWrapper;
import com.gcxy.config.warpper.RequestBodyWrapper;
import com.gcxy.constant.PermissionConst;
import com.gcxy.entity.Resource;
import com.gcxy.entity.User;
import com.gcxy.utils.HttpContextUtils;
import com.gcxy.utils.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import org.springframework.util.AntPathMatcher;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 权限控制：判断用户角色
 *
 * @author pep7chiao
 * @date 2024/7/7
 */
@Slf4j
@Component
public class AccessDecisionManagerImpl implements AccessDecisionManager {

    @Value("${logined.white.list}")
    private List<String> whiteList;

    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        //获取用户信息
        Object principal = authentication.getPrincipal();
        User user = null;
        if (principal instanceof User) {
            user = (User) authentication.getPrincipal();
            if (ObjectUtils.isEmpty(user)) {
                // 验证通过
                throw new AuthorizationServiceException("请重新登录");
            }
        } else {
            throw new AuthorizationServiceException("密码错误！");
        }
        //根据请求判断权限
        if (object instanceof FilterInvocation) {
            FilterInvocation filterInvocation = (FilterInvocation) object;
            String requestURI = filterInvocation.getRequestUrl();
            //判断请求路径是否在白名单中
            if (SecurityUtil.isAllowedListContains(requestURI, whiteList)) {
                return;
            }
            // 获取请求对象
            HttpServletRequest httpRequest = filterInvocation.getRequest();
            // 获取请求内容类型
            String contentType = httpRequest.getContentType();
            //判断是否是文件上传
            if (ObjectUtils.isEmpty(contentType) || !contentType.startsWith("multipart/form-data")) {
                // 验证参数中是否有agencyId
                String agencyId = httpRequest.getParameter("agencyId");
                if (ObjectUtils.isEmpty(agencyId)) {
                    String bodyString = HttpContextUtils.getBodyString(httpRequest);
                    try {
                        Map bodyMap = JSON.parseObject(bodyString, HashMap.class);
                        if (ObjectUtils.isNotEmpty(bodyMap) && bodyMap.containsKey("agencyId")) {
                            agencyId = bodyMap.get("agencyId").toString();
                        }
                    } catch (Exception e) {
                        log.error("传入body内容{}，内容异常信息:{}", bodyString, e.getMessage());
                    }
                }
                // 参数中有agencyId
                if (agencyId != null) {
                    Integer agencyIdFromRequest = null;
                    try {
                        if (!"".equals(agencyId.trim())) {
                            agencyIdFromRequest = Integer.parseInt(agencyId);
                        }
                    } catch (NumberFormatException e) {
                        throw new AccessDeniedException("agencyId参数错误");
                    }
                    // 验证两个agencyId不相等，且该用户没有操作其他组织机构的权限
                    List<Resource> userResource = user.getResourceList();
                    boolean flag = false;
                    if (ObjectUtils.isNotEmpty(userResource)) {
                        String otherPath = PermissionConst.OPERATING_OTHER + httpRequest.getRequestURI().trim();
                        flag = userResource.stream().filter(Objects::nonNull).anyMatch(resource -> new AntPathMatcher().match(resource.getResourcePath().trim(), otherPath));
                    }
                    if (!ObjectUtil.equals(agencyIdFromRequest, user.getAgencyId()) && !flag) {
                        throw new AccessDeniedException("无权限访问此资源");
                    }
                } else if (!ObjectUtils.isEmpty(user.getAgencyId())) { //参数中没有agencyId，但用户有agencyId
                    httpRequest.setAttribute("agencyId", user.getAgencyId());
                } //参数和用户中都没有agencyId
            }
        }

        for (ConfigAttribute configAttribute : collection) {
            // 当前url所需要的角色
            String needRole = configAttribute.getAttribute();
            // 判断用户角色是否为url所需角色
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            // 遍历当前的权限集合
            for (GrantedAuthority authority : authorities) {
                // 如果权限集合中的角色与当前url所需要的角色一致
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足，请联系管理员！");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return false;
    }
}
